package com.ricchieri.dao;

import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;

import com.ricchieri.common.BusinessSettings;
import com.ricchieri.dao.interf.IReservationDao;
import com.ricchieri.filter.ReservationFilter;
import com.ricchieri.model.Reservation;
import com.ricchieri.util.DateUtil;
import com.ricchieri.util.StringUtils;

public class ReservationDao extends GenericDao implements IReservationDao {
	private static final long serialVersionUID = -5566094266910635082L;
	
	private static final String SORT_TRIP = "sortTrip";
	private static final String SORT_TIME = "sortTime";
	private static final String SORT_FULLNAME = "sortFullname";
	private static final String SORT_DOCUMENT_TYPE = "sortDocumentType";
	private static final String SORT_DNI = "sortDni";
	
	private boolean joinUser = Boolean.FALSE;
	private boolean joinDocumentType = Boolean.FALSE;
	private boolean joinTrip = Boolean.FALSE;
	private boolean joinSchedule = Boolean.FALSE;
	

	@Override
	@SuppressWarnings("unchecked")
	public List<Reservation> getAllReservation(ReservationFilter reservationFilter) {
		Criteria criteria = criteriaAllReservation(reservationFilter);		
		return criteria.list();
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<Reservation> getAllReservationPaginatedByFilter(
			ReservationFilter reservationFilter, int startRecord) {
		Criteria criteria = criteriaAllReservation(reservationFilter);
		criteria.setMaxResults(BusinessSettings.ROW_MAX_RESULT);
		criteria.setFirstResult(startRecord);
		
		return criteria.list();
	}

	@Override
	public int getAllReservationCountByFilter(ReservationFilter reservationFilter) {
		Criteria criteria = criteriaAllReservation(reservationFilter);
		Long result = (Long)criteria.setProjection(Projections.rowCount()).uniqueResult();
		return new Integer(result.intValue());
	}
	
	@Override
	public Reservation getReservationById(ReservationFilter reservationFilter) {
		Criteria criteria = criteriaAllReservation(reservationFilter);
		
		return (Reservation) criteria.list().get(0);
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public List<Object[]> getCountReservationByOperator(ReservationFilter reservationFilter) {
		Criteria criteria = criteriaAllReservation(reservationFilter);
		criteria.createAlias("reservation.operator", "operator");		
		criteria.createAlias("operator.roles", "roles");
		criteria.createAlias("roles.role", "role");
		criteria.add(Restrictions.eq("role.description", BusinessSettings.ROLE_OPERATOR));
		
		ProjectionList projectionList = Projections.projectionList();
		projectionList.add(Projections.groupProperty("operator.id"));
		projectionList.add(Projections.rowCount());
		criteria.setProjection(projectionList);
		
		return criteria.list();
	}
	
	
	private void resetJoins() {
		joinUser = Boolean.FALSE;
		joinDocumentType = Boolean.FALSE;
		joinSchedule = Boolean.FALSE;
		joinTrip = Boolean.FALSE;
	}
	
	private Criteria criteriaAllReservation(ReservationFilter reservationFilter) {
		Criteria criteria = super.criteriaAllBasicWithoutOrder(reservationFilter, Reservation.class);
		resetJoins();
		
		joinUser = StringUtils.isNotEmpty(reservationFilter.getDni()) 
				|| StringUtils.isNotEmpty(reservationFilter.getFirstName())
				|| StringUtils.isNotEmpty(reservationFilter.getLastName());
				
		if(StringUtils.isNotEmpty(reservationFilter.getReservationId())){
			criteria.add(Restrictions.eq(getTableName().concat(".id"), reservationFilter.getReservationById()));
		}
		
		if(joinUser) {
			
			criteria.createAlias(getTableName().concat(".user"), "user");
			if(StringUtils.isNotEmpty(reservationFilter.getDni())) {
				criteria.add(Restrictions.eq("user.dni", reservationFilter.getDni()));
			}
			
			if(StringUtils.isNotEmpty(reservationFilter.getFirstName())) {
				criteria.add(Restrictions.like("user.firstName", "%".concat(reservationFilter.getFirstName()).concat("%")));		
			}
			
			if(StringUtils.isNotEmpty(reservationFilter.getLastName())) {
				criteria.add(Restrictions.like("user.lastName", "%".concat(reservationFilter.getLastName()).concat("%")));	
			}
		}
		
		if(StringUtils.isNotEmpty(reservationFilter.getDocumentType())) {
			
			if(!joinUser) {
				criteria.createAlias(getTableName().concat(".user"), "user");
			}

			criteria.createAlias("user.documentType", "documentType");
			criteria.add(Restrictions.eq("documentType.id", reservationFilter.getDocumentTypeId()));
			joinDocumentType = Boolean.TRUE;
		}
		
		if(StringUtils.isNotEmpty(reservationFilter.getTrip())) {
			criteria.createAlias(getTableName().concat(".scheduleCharter"), "scheduleCharter");
			criteria.createAlias("scheduleCharter.trip", "trip");
			criteria.add(Restrictions.eq("trip.id", reservationFilter.getTripId()));
			joinTrip = Boolean.TRUE;
		}
		
		if(StringUtils.isNotEmpty(reservationFilter.getReservationType())) {
			criteria.createAlias(getTableName().concat(".reservationType"), "reservationType");
			criteria.add(Restrictions.eq("reservationType.id", reservationFilter.getReservationTypeId()));
		}
		
		if (reservationFilter.getStart() != null) {
			if(!joinTrip) {
				criteria.createAlias(getTableName().concat(".scheduleCharter"), "scheduleCharter");
			}
			criteria.createAlias("scheduleCharter.schedule", "schedule");
			
			criteria.add(Restrictions.ge("schedule.date", DateUtil.changeToStartDay(reservationFilter.getStart())));
			joinSchedule = Boolean.TRUE;
		}
		
		if(reservationFilter.getFinish() != null) {
			if(!joinSchedule) {
				if(!joinTrip) {
					criteria.createAlias(getTableName().concat(".scheduleCharter"), "scheduleCharter");
				}
				criteria.createAlias("scheduleCharter.schedule", "schedule");
			}
			criteria.add(Restrictions.le("schedule.date", DateUtil.changeToEndDay(reservationFilter.getFinish())));
			joinSchedule = Boolean.TRUE;
		}

		if(StringUtils.isNotEmpty(reservationFilter.getCancel())) {
			criteria.add(Restrictions.eq(getTableName().concat(".cancel"), Boolean.parseBoolean(reservationFilter.getCancel())));
		}
		
		if(reservationFilter.getSortField() != null && !reservationFilter.getSortField().equals("")) {
			criteria  = orderResults(criteria, reservationFilter);
		}
		
		if(reservationFilter.getScheduleCharterId() > 0) {
			if(!joinSchedule) {
				criteria.createAlias(getTableName().concat(".scheduleCharter"), "scheduleCharter");
			}
			criteria.add(Restrictions.eq("scheduleCharter.id", reservationFilter.getScheduleCharterId()));
			joinSchedule = Boolean.TRUE;
		}
		
		return criteria;
	}
	
	private Criteria orderResults(Criteria criteria, ReservationFilter reservationFilter) {
		String orderField = reservationFilter.getSortField();
		
		if(orderField.equals(SORT_DNI)) {
			if(!joinUser) {
				criteria.createAlias(getTableName().concat(".user"), "user");
			}
			
			addOrder(reservationFilter, "user.dni", criteria);
			
		} else if(orderField.equals(SORT_DOCUMENT_TYPE)) {
			
			if(!joinUser) {
				criteria.createAlias(getTableName().concat(".user"), "user");
			}
			
			if(!joinDocumentType) {
				criteria.createAlias("user.documentType", "documentType");
			}
			
			addOrder(reservationFilter, "documentType.id", criteria);
			
		} else if(orderField.equals(SORT_FULLNAME)) {
			
			if(!joinUser) {
				criteria.createAlias(getTableName().concat(".user"), "user");
			}
			
			addOrder(reservationFilter, "user.lastName", criteria);
			addOrder(reservationFilter, "user.firstName", criteria);
		
		} else if(orderField.equals(SORT_TIME)) {
			if(!joinSchedule) {
				criteria.createAlias(getTableName().concat(".scheduleCharter"), "scheduleCharter");
				criteria.createAlias("scheduleCharter.schedule", "schedule");		
			}
			addOrder(reservationFilter, "schedule.date", criteria);
			addOrder(reservationFilter, "schedule.time", criteria);
			
		} else if(orderField.equals(SORT_TRIP)) {
			if(!joinTrip) {
				if(!joinSchedule) {
					criteria.createAlias(getTableName().concat(".scheduleCharter"), "scheduleCharter");
				}
				criteria.createAlias("scheduleCharter.trip", "trip");
			}
			addOrder(reservationFilter, "trip.id", criteria);
			
		} else {
			addOrder(reservationFilter, getTableName().concat(".").concat(reservationFilter.getSortField()), criteria);
		}
		return criteria;
	}

	@Override
	protected String getTableName() {
		return "reservation";
	}
}
